/**
 * 题目:
 * 实现Promise A+ 规范(简易版)
 * 
 * 描述: 
 * Promise A+规范: Promise是构造函数，实例 p 是一个对象或函数，有一个then方法，满足相关条件，就是一个Promise
 * ES6的Promise: 实现Promise A+规范，并扩展了 catch, finally, all 这些方法。
 * Promise A+英文原文: https://promisesaplus.com/
 * Promise A+中文翻译: https://www.ituring.com.cn/article/66566
 * 
 * 参考:
 * https://www.bilibili.com/video/BV1au411b7mt/?spm_id_from=333.999.0.0
 * https://github.com/Sunny-117/js-challenges/issues/8
*/

// 定义 promise 状态
const PENDING = 'pending'
const FULFIllED = 'fulFilled'
const REJECTED = 'rejected'

// 判断是否为函数
function isFunction(fn) {
  return typeof fn === 'function'
}

/**
 * 运行一个微队列任务
 * 把传递的函数放到微队列中
 * @param {Function} cb
*/
function runMicroTask(cb) {
  if (typeof process !== 'undefined') {
    // node 环境 process.nextTick  模拟
    process.nextTick(cb)
  } else if (typeof MutationObserver !== 'undefined') { 
    // 用MutationObserver模拟
    const observe = new MutationObserver(cb)
    const textNode = document.createTextNode('1')
    observe.observe(textNode, { characterData: true })
    textNode.data = '2'
  } else { // 用计时器模拟
    setTimeout(cb, 0)
  }
}

class MyPromise {
  #state = PENDING // 状态(属性带#, 私有属性)
  #result = undefined // 数据
  #handlers = [] // 处理函数形成的队列

  /**
   * 创建一个Promise
   * @param {Function} executor 执行器函数，立即执行
   */
  constructor(excutor) {
    excutor(this.#resolve.bind(this), this.#reject.bind(this))
  }

   /**
   * 更改任务状态(方法带#, 私有方法)
   * @param {String} state 新状态
   * @param {any} result 相关数据
   */
  #changeState(state, result) {
    // 当前状态是 fulFilled / rejected, 则不需要改变状态
    if (this.#state !== PENDING) return
    this.#state = state
    this.#result = result
    this.#runHandlers() // 状态变化，执行队列
  }

   /**
   * 标记当前任务完成
   * @param {any} value 任务完成的相关数据
   */
  #resolve(value) {
    this.#changeState(FULFIllED, value)
  }

  /**
   * 标记当前任务失败
   * @param {any} reason 任务失败的相关数据
   */
  #reject(reason) {
    this.#changeState(REJECTED, reason)
  }

  /**
   * 处理一个handler
   * @param {Function} callback onFulfilled | onRejected
   * @param {Function} resolve 成功回调
   * @param {Function} reject 失败回调
  */
  #runOnece(callback, resolve, reject) {
    if (!isFunction(callback)) { // 判断then方法传没传回调
      const fn = this.#state === FULFIllED ? resolve : reject
      fn(this.#result)
    } else {
      const data = callback(this.#result)
      resolve(data)
    }
  }

  /**
   * 根据实际情况，执行队列
  */
  #runHandlers(callback, resolve, reject) {
    // 当前状态是 pending 不执行handlers
    if (this.#state === PENDING) return 
    // 执行微任务
    runMicroTask(() => {
      while( this.#handlers.length) {
        const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift()
        try {
          if (this.#state === FULFIllED) {
            this.#runOnece(onFulfilled, resolve, reject)
          } else if (this.#state === REJECTED) {
            this.#runOnece(onRejected, resolve, reject)
          }
        } catch(error) {
          reject(error)
        }
      }
    })
  }

   /**
   * Promise A+规范的then(原型方法)
   * @param {Function} onFulfilled
   * @param {Function} onRejected
   */
  then(onFulfilled, onRejected) {
    // 返回新的Promise构造函数
    return new MyPromise((resolve, reject) => {
      // 存一下onFulfilled, onRejected, resolve, reject
      this.#handlers.push({ onFulfilled, onRejected, resolve, reject })
      // 取出来执行
      this.#runHandlers()
    })
  }
}


// // 测试：excutor 执行是同步的
// const p1 = new MyPromise(() => {
//   console.log('excutor')
// })
// console.log(1)
// // 'excutor'
// // 1

// // 测试：先调resolve, 再调reject不可以，状态不可逆
// const p2 = new MyPromise((resolve, reject) => {
//   resolve(2.1)
//   reject(2.2) // 不执行
// })

// // 测试then方法: then 方法的onFulfilled，onRejected什么时候执行
// const p3 = new MyPromise((resolve, reject) => {
//   setTimeout(()=>{
//     resolve(3.1)
//   }, 1000)
//   // reject(3.2)
// })
// p3.then(
//   (res) => { console.log('成功1', res) }, // 等resolve执行了, 执行 onFulfilled 回调
//   (err) => { console.log('失败1', err) }  // 等reject执行了, 执行 onRejected 回调
// )
// p3.then( // then可多次调用，多次调用，#handlers 取除回调一一执行
//   (res) => { console.log('成功2', res) }, 
//   (err) => { console.log('失败2', err) }  
// )

// // 测试then方法：传的onFulfilled, onRejected不是函数怎么办? resolve 的结果会穿透到下一个then方法里面
// const p4 = new MyPromise((resolve, reject) => {
//   resolve(4.1)
// })
// p4.then(null, 11).then(res => {
//   console.log(res) 
// })
// console.log(4.2)
// // 4.2
// // 4.1
